<template>
  <div
    id="code-editor"
    ref="codeEditorRef"
    style="min-height: 400px; height: 60vh"
  />
</template>

<script setup lang="ts">
import * as monaco from "monaco-editor";
import { onMounted, ref, toRaw, withDefaults, defineProps, watch } from "vue";

/**
 * 定义组件属性类型
 */
interface Props {
  value: string;
  language?: string;
  handleChange: (v: string) => void;
  defaultCode?: string;
}

/**
 * 指定组件初始值
 */
const props = withDefaults(defineProps<Props>(), {
  value: () => "",
  language: () => "java",
  handleChange: (v: string) => {
    console.log(v);
  },
  defaultCode: () => "",
});

const codeEditorRef = ref();
const codeEditor = ref();

watch(
  () => props.language,
  () => {
    if (codeEditor.value) {
      monaco.editor.setModelLanguage(
        toRaw(codeEditor.value).getModel(),
        props.language
      );
    }
  }
);
onMounted(() => {
  if (!codeEditorRef.value) {
    return;
  }
  codeEditor.value = monaco.editor.create(codeEditorRef.value, {
    value: props.defaultCode,
    language: props.language,
    automaticLayout: true,
    colorDecorators: true,
    minimap: {
      enabled: true,
    },
    readOnly: false,
    theme: "vs-dark",
  });

  codeEditor.value.onDidChangeModelContent(() => {
    /*
    toRaw(codeEditor.value) 先将 codeEditor.value 转换为原始值。
    然后在原始值上调用 getValue 方法获取编辑器的内容。
    最后将获取到的内容传递给 props.handleChange。
     */
    props.handleChange(toRaw(codeEditor.value).getValue());

    /*
     props.handleChange(toRaw(codeEditor.value.getValue()));
     codeEditor.value.getValue() 先调用 getValue 方法获取编辑器的内容，
     toRaw 将获取到的内容转换为原始值（如果它是响应式对象）。
     最后将转换后的值传递给 props.handleChange。
     */

    /**
     * 响应式对象的陷阱:
     * 如果 codeEditor.value 是一个响应式对象，那么 codeEditor.value.getValue() 返回的值可能是另一个响应式对象。
     * 在 toRaw 调用之前，getValue 方法可能已经触发了响应式系统的某些操作，导致不必要的性能开销。
     * 如果 getValue 方法内部有复杂的逻辑或频繁的响应式操作，这可能会导致性能瓶颈，从而导致编辑器卡死。
     * toRaw 的作用:
     * toRaw 用于将响应式对象转换为普通对象，避免响应式系统的干扰。
     * 如果 codeEditor.value 本身是响应式对象，但在 getValue 之前将其转换为普通对象，可以减少不必要的响应式操作，提高性能。
     *
     * 提前转换为普通对象:
     * toRaw(codeEditor.value) 先将 codeEditor.value 转换为普通对象。
     * 这样在调用 getValue 方法时，操作的是普通对象，而不是响应式对象。
     * 因此，getValue 方法的执行不会触发额外的响应式系统操作，减少了性能开销。
     * 减少响应式系统的影响:
     * 通过提前将 codeEditor.value 转换为普通对象，可以避免在 getValue 方法内部进行复杂的响应式操作，从而提高整体性能
     */
  });
});
</script>

<style scoped></style>
